package
{
	import flash.display.*;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.sampler.NewObjectSample;
	import flash.text.*;
	import flash.ui.Mouse;
	
	
	/** Creates a box that displays a message somewhere on this item */
	public class MessageBox extends MovieClip
	{
		// public constants
		public static const OPACITY:Number = .97;
		public static const PERSISTENT_TYPE:String = "persistent";
		public static const EPHEMERAL_TYPE:String = "ephemeral";
		public static const ANIMATION_TIME:uint = 300; // milliseconds
		public static const X_PADDING:Number = 4, TITLE_HEIGHT:Number = 24, BOUNCE_WIDTH:Number = 10;
		public static const OPTIMIZATION_RATIO:Number = .5; // the optimum width is .5 of the maximum width
		// public vars
		public var WIDTH:Number, HEIGHT:Number, MAX_WIDTH:Number, MAX_HEIGHT:Number;
		public var type:String, id:String;
		// private vars
		private var initx:Number, inity:Number;
		private var wstep:Number, hstep:Number; // the increase in width and height on each frame of the popup
		private var curWidth:Number, curHeight:Number;
		// private objects
		private var tfield:TextField;
		private var dragbar:Sprite, xbox:Sprite;
		private var reachedBounce:Boolean = false; // part of a fun animation
		private var mouseDownPoint:Point;
		
		public function get message():String{if (this.tfield != null){ return this.tfield.text;}else{return "";}}
		/** Sets up variables and initiates the growing animation */
		public function MessageBox(_message:String, initx:Number, inity:Number, type:String, id:String="", MAX_WIDTH:Number=Number.MAX_VALUE, MAX_HEIGHT:Number=Number.MAX_VALUE)
		{
			this.id = id;
			this.initx = initx;
			this.inity = inity;
			this.type = type;
			this.MAX_WIDTH = MAX_WIDTH;
			this.MAX_HEIGHT = MAX_HEIGHT;
			// setup tfield, but don't add until after pop-up animation
			tfield = new TextField();
			tfield.selectable = false;
			tfield.mouseEnabled = false;
			tfield.multiline = true;
			tfield.wordWrap = true;
			tfield.defaultTextFormat = new TextFormat("Arial", 20, 0x444444, null, null, null, null, null, "left");
			tfield.text = _message;		
			// beginning with the optimum width, attempt to fit everything in a readable text box,
			// if the optimum width does not work, then expand
			var maxWidth:Number = MAX_WIDTH-2*X_PADDING;
			var maxHeight:Number = MAX_HEIGHT - TITLE_HEIGHT - X_PADDING;
			var optimumWidth:Number = maxWidth*OPTIMIZATION_RATIO;
			var rangeWidth:Number = maxWidth - optimumWidth;
			var widthFound:Boolean = false;
			for (var ow:Number=optimumWidth; ow < maxWidth;  ow+=.1*rangeWidth)
			{
				tfield.width = ow;
				if (tfield.textHeight <= maxHeight)
				{
					tfield.width = tfield.textWidth+10;
					tfield.height = tfield.textHeight+10;
					widthFound = true;
					break;
				}
			}
			/** Too much damn text, oh well, get as much visible as possible */
			if (!widthFound)
			{
				tfield.width = maxWidth;
				tfield.height = maxHeight;
			}
			
			WIDTH = tfield.width + 2*X_PADDING;
			HEIGHT = tfield.height + X_PADDING + TITLE_HEIGHT;
			this.addEventListener(Event.ADDED, handleAdded);
		}
		private function handleAdded(event:Event):void
		{
			this.removeEventListener(Event.ADDED, handleAdded);
			this.x = initx;
			this.y = inity;
			// initiate "pop-up"
			var fps:uint = this.stage.frameRate;		
			wstep = WIDTH/(fps*ANIMATION_TIME/1000);
			hstep = HEIGHT/(fps*ANIMATION_TIME/1000);
			curWidth = 0;
			curHeight = 0;
			
			this.addEventListener(Event.ENTER_FRAME, handlePopUpAnimation);
		}
		/** A cool animation of this message poping up from nothing */
		private function handlePopUpAnimation (event:Event):void
		{
			if (!reachedBounce)
			{
				curWidth += wstep;
				curHeight += hstep;
				// adjust size
				drawMessageBox(curWidth, curHeight); // initial size will be one step
				// keep growing unless we have reached our target size
				if (this.width >= WIDTH + BOUNCE_WIDTH)
				{
					reachedBounce = true;
				}
			} else
			{
				curWidth -= wstep;
				curHeight -= hstep;
				// adjust size
				drawMessageBox(curWidth, curHeight); // initial size will be one step
				
				if (this.width <= WIDTH)
				{
					this.removeEventListener(Event.ENTER_FRAME, handlePopUpAnimation);	
					// add the textField
					drawMessageBox(WIDTH, HEIGHT); // initial size will be one step
					if (type == MessageBox.PERSISTENT_TYPE)
					{
						// add a listener to this so we can use the mouse
						this.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverThisPersistent);
						dragbar = new Sprite();
						dragbar.graphics.beginFill(0x888888, .9);
						dragbar.graphics.drawRect(0,0,WIDTH-TITLE_HEIGHT,TITLE_HEIGHT);
						dragbar.graphics.endFill();
						dragbar.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDownBar);
						//dragbar.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOver);
						this.addChild(dragbar);
						xbox = new Sprite();
						xbox.graphics.lineStyle(2, 0x888888, .9);
						xbox.graphics.beginFill(0xFFFFFF, 0);
						xbox.graphics.drawRect(0,0,TITLE_HEIGHT-1,TITLE_HEIGHT-1);
						xbox.graphics.endFill();
						xbox.graphics.lineStyle(2, 0x222222, .9);
						xbox.graphics.moveTo(2,2);
						xbox.graphics.lineTo(TITLE_HEIGHT-2,TITLE_HEIGHT-2);
						xbox.graphics.moveTo(2, TITLE_HEIGHT-2);
						xbox.graphics.lineTo(TITLE_HEIGHT-2,2);
						xbox.y = (dragbar.height-xbox.height)/2;
						xbox.x = WIDTH-xbox.width+1;
						xbox.addEventListener(MouseEvent.CLICK, handleClickXBox);
						//xbox.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOver);
						this.addChild(xbox);
						tfield.x = X_PADDING;
						tfield.y = TITLE_HEIGHT;
						this.addChild(tfield);
					} else if (type == MessageBox.EPHEMERAL_TYPE)
					{
						// add a listener to this so we can use the mouse
						this.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverThisEphemeral);
						tfield.x = X_PADDING;
						tfield.y = (HEIGHT - tfield.height)/2;
						this.addChild(tfield);
					}
					GeometryGameItem(parent).processCommand("message", "initiate", this, new Array(this.id));
				}
			}			
		}
		private function drawMessageBox(_width:Number, _height:Number):void
		{
			if (type == PERSISTENT_TYPE)
			{
				this.graphics.clear();
				this.graphics.lineStyle(2,0x888888,OPACITY);
				this.graphics.beginFill(0xFFFFFF, OPACITY);
				this.graphics.drawRect(0,0,_width, _height); // initial size will be one step
				this.graphics.endFill();
			} else if (type == EPHEMERAL_TYPE)
			{
				this.graphics.clear();
				this.graphics.lineStyle(2,0x888888,OPACITY);
				this.graphics.beginFill(0xFFFFFF, OPACITY);
				//this.graphics.drawRect(0,0,_width, _height); // initial size will be one step
				this.graphics.drawRoundRect(0,0,_width, _height, TITLE_HEIGHT*2, TITLE_HEIGHT*2); // initial size will be one step
				this.graphics.endFill();
			}
		}
		
		/////////////////////////////////// INTERACTIVITY //////////////////////////////
		//////////////////////////////////   PERSISTENT TYPE //////////////////////////////
		private function handleMouseOverThisPersistent (event:MouseEvent):void
		{
			this.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOverThisPersistent);
			Mouse.show();
			this.addEventListener(MouseEvent.MOUSE_OUT, handleMouseOutThis);			
		}
		private function handleMouseOutThis (event:MouseEvent):void
		{
			this.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutThis);	
			this.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverThisPersistent);			
		}
		////////// DRAG BAR ///////////////
		/** When user presses down on mouse bar, we set a drag flag and add a frame handler */
		private function handleMouseDownBar (event:MouseEvent):void
		{
			// remove this down handler
			dragbar.removeEventListener(MouseEvent.MOUSE_DOWN, handleMouseDownBar);
			mouseDownPoint = new Point(this.mouseX, this.mouseY);
			// add new listeners
			this.stage.addEventListener(MouseEvent.MOUSE_UP, handleMouseUpBar);
			this.addEventListener(Event.ENTER_FRAME, handleDraggingFrame);
		}
		/** When user releases mouse bar, we remomve drag flag and frame handler */
		private function handleMouseUpBar (event:MouseEvent):void
		{
			// remove this down handler
			this.stage.removeEventListener(MouseEvent.MOUSE_UP, handleMouseUpBar);
			this.removeEventListener(Event.ENTER_FRAME, handleDraggingFrame);
			// add new listeners
			dragbar.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDownBar);			
		}
		/** Move this based on parent's mouse position */
		private function handleDraggingFrame (event:Event):void
		{
			this.x = parent.mouseX - mouseDownPoint.x;
			this.y = parent.mouseY - mouseDownPoint.y;
		}
		
		//////// X BOX ////////////
		/** When user clicks on this box, remove this from the stage */
		private function handleClickXBox (event:MouseEvent):void
		{
			xbox.removeEventListener(MouseEvent.CLICK, handleClickXBox);
			if (dragbar.hasEventListener(MouseEvent.MOUSE_DOWN)) dragbar.removeEventListener(MouseEvent.MOUSE_DOWN, handleMouseDownBar);	
			if (this.stage.hasEventListener(MouseEvent.MOUSE_UP)) this.stage.removeEventListener(MouseEvent.MOUSE_UP, handleMouseUpBar);
			if (this.hasEventListener(Event.ENTER_FRAME)) this.removeEventListener(Event.ENTER_FRAME, handleDraggingFrame);
			if (this.hasEventListener(MouseEvent.MOUSE_OVER)) this.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOverThisPersistent);
			if (this.hasEventListener(MouseEvent.MOUSE_OUT)) this.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutThis);
			Mouse.hide();
			GeometryGameItem(parent).processCommand("message", "complete", parent, new Array(id));
			this.parent.removeChild (this);		
		}
		
		//////////////////////////// EPHEMERAL TYPE /////////////////////
		/** When user simply mouses over this, pop, it goes away */
		private function handleMouseOverThisEphemeral(event:MouseEvent):void
		{
			this.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOverThisEphemeral);
			GeometryGameItem(parent).processCommand("message", "complete", parent, new Array(id));
			this.parent.removeChild(this);
		}
	}
}